#include "av.h"

#define FLAG_METATABLE "av flag metatable"

typedef struct lua_flag_s {
	int value;
	lua_constant * constants;
} lua_flag;

static
int lua_flag_type_index(lua_State * L) {
	lua_flag * flag = (lua_flag *) lua_touserdata(L, 1);
	const char * key = lua_tostring(L, 2);
	lua_constant * entry = flag->constants;
	while (entry->name) {
		if (strcmp(entry->name, key) == 0) {
			lua_pushboolean(L, flag->value & entry->value);
			return 1;
		}
		entry++;
	}
	luaL_error(L, "Invalid flag key");
	return 0;
}

static
int lua_flag_type_newindex(lua_State * L) {
	lua_flag * flag = (lua_flag *) lua_touserdata(L, 1);
	const char * key = lua_tostring(L, 2);
	int value = lua_toboolean(L, 3);
	lua_constant * entry = flag->constants;
	while (entry->name) {
		if (strcmp(entry->name, key) == 0) {
			if (value) {
				flag->value |= entry->value;
			} else {
				flag->value &= ~(entry->value);
			}
			return 1;
		}
		entry++;
	}
	luaL_error(L, "Invalid flag key");
	return 0;
}

static
int lua_flag_type_call(lua_State * L) {
	lua_flag * flag = (lua_flag *) lua_touserdata(L, 1);
	int top = lua_gettop(L);
	switch(top) {
	case 1:
		lua_pushinteger(L, flag->value);
		return 1;
	case 2:
		if (lua_isfunction(L, 2)) {
			lua_constant * entry = flag->constants;
			while (entry->name) {
				lua_pushvalue(L, 2);
				lua_pushstring(L, entry->name);
				lua_pushboolean(L, flag->value & entry->value);
				lua_call(L, 2, 0);
				entry++;
			}
		} else {
			flag->value = luaL_checkint(L, 2);
		}
		return 0;
	default:
		luaL_error(L, "invalid number of arguments for flag call handler");
		return 0;
	}
}

extern
int lua_register_av_flag_type(lua_State *L) {
	luaL_newmetatable (L, FLAG_METATABLE);

	lua_pushstring (L, "__index");
	lua_pushcfunction (L, lua_flag_type_index);
	lua_settable (L, -3);
	lua_pushstring (L, "__newindex");
	lua_pushcfunction (L, lua_flag_type_newindex);
	lua_settable (L, -3);
	lua_pushstring (L, "__call");
	lua_pushcfunction (L, lua_flag_type_call);
	lua_settable (L, -3);

	return 1;
}

extern
void lua_push_flag(lua_State * L, int value, lua_constant * constants) {
	lua_flag *f = (lua_flag *) lua_newuserdata (L, sizeof(lua_flag));
	f->value = value;
	f->constants = constants;
	luaL_getmetatable (L, FLAG_METATABLE);
	lua_setmetatable (L, -2);
}

